////
/// @group helpers/layout
////

@import "../settings/media-queries";
@import "../tools/px-to-em";

// =========================================================
// Helpers
// =========================================================

/// Get the value of a breakpoint by name.
///
/// @param {String | Number} $value - If a string, the name of a breakpoint
///   in $breakpoints. If a number without units, it will convert to px. If a
///   number with units, it will return the value unaltered.
/// @param {Map} $breakpoints [$govuk-breakpoints] - The map to look for $value.
/// @returns {Number} - The set (minimum) value of the breakpoint
///
/// @example scss
///  .element {
///    width: govuk-breakpoint-value(tablet);
///    @media (min-width: #{govuk-breakpoint-value(desktop)}) {
///      color: red;
///    }
///    @media (min-width: #{govuk-breakpoint-value(400px)}) {
///      color: green;
///    }
///    $custom-breakpoint-map: (
///      small: 350px,
///      medium: 769px,
///      large: 1100px,
///      extra-large: 1600px
///    );
///    @media (orientation: landscape) and (min-width: #{govuk-breakpoint-value(extra-large, $custom-breakpoint-map)}) {
///      color: blue;
///    }
///  }
///
/// @access public

@function govuk-breakpoint-value($value, $breakpoints: $govuk-breakpoints) {
  // If $value is a number
  @if type-of($value) == "number" {
    // If the number is unitless, coerce it into a pixel value
    @if unitless($value) {
      $value: $value * 1px;
    }

    @return $value;
  }

  // If $value is a string and exists as a key in in $breakpoints,
  // look up and use the value of it
  @if type-of($value) == "string" and map-has-key($breakpoints, $value) {
    @return map-get($breakpoints, $value);
  }

  // If we get this far, we can't use the value, so return an error
  @error "Could not find a breakpoint given `#{$value}`.";
}

/// Generate the `min-width` segment of a media query given a breakpoint key
///
/// Pixel values are converted to ems for backwards compatibility with
/// sass-mq. Unlike sass-mq, non-px and em values can be used as well.
///
/// @param {String | Number} $from - If a string, expects the name of a
///   breakpoint in $breakpoints. If a number, it will use that number.
/// @param {Map} $breakpoints [$govuk-breakpoints] - The map to look for $from.
/// @returns {String} - A `min-width` media query segment
///
/// @example scss
///  .example {
///    @media #{govuk-from-breakpoint(tablet)} {
///      color: red;
///    }
///    @media #{govuk-from-breakpoint(30em)} {
///      color: green;
///    }
///    @media #{govuk-from-breakpoint(tablet)} and (orientation: landscape) {
///      color: blue;
///    }
///    $custom-breakpoint-map: (
///      small: 350px,
///      medium: 769px,
///      large: 1100px,
///      extra-large: 1600px
///    );
///    @media #{govuk-from-breakpoint(extra-large, $custom-breakpoint-map)} {
///      color: cyan;
///    }
///  }
///
/// @access public

@function govuk-from-breakpoint($from, $breakpoints: $govuk-breakpoints) {
  $value: govuk-breakpoint-value($from, $breakpoints);

  @if type-of($value) == "number" and unit($value) == "px" {
    $value: govuk-em($value);
  }

  @return "(min-width: #{$value})";
}

/// Generate the `max-width` segment of a media query given a breakpoint key
///
/// sass-mq converted pixel values to ems, and only performed subtractions on
/// named breakpoints. These have been retained for backwards compatibility,
/// though unlike sass-mq, this also supports using non-px and em values.
///
/// @param {String | Number} $until - If a string, expects the name of a
///   breakpoint in $breakpoints. If a number, it will use that number.
/// @param {Map} $breakpoints [$govuk-breakpoints] - The map to look for $until.
/// @returns {String} - A `max-width` media query segment
///
/// @example scss
///  .example {
///    @media #{govuk-until-breakpoint(desktop)} {
///      color: red;
///    }
///    @media #{govuk-until-breakpoint(40em)} {
///      color: green;
///    }
///    @media #{govuk-until-breakpoint(tablet)} and (orientation: landscape) {
///      color: blue;
///    }
///    $custom-breakpoint-map: (
///      small: 350px,
///      medium: 769px,
///      large: 1100px,
///      extra-large: 1600px
///    );
///    @media #{govuk-until-breakpoint(extra-large, $custom-breakpoint-map)} {
///      color: cyan;
///    }
///  }
///
/// @access public

@function govuk-until-breakpoint($until, $breakpoints: $govuk-breakpoints) {
  $value: govuk-breakpoint-value($until, $breakpoints);

  @if type-of($value) == "number" and unit($value) == "px" {
    // If it's a pixel value, convert it to ems.
    $value: govuk-em($value);
  }

  // If $value derives from a named breakpoint, additionally subtract .01em
  @if type-of($until) != "number" and unit($value) == "em" {
    $value: $value - 0.01em;
  }

  @return "(max-width: #{$value})";
}

/// Media query
///
/// @param {String | Boolean} $from [false] - One of $breakpoints
/// @param {String | Boolean} $until [false] - One of $breakpoints
/// @param {String | Boolean} $and [false] - Additional media query parameters
/// @param {String} $media-type [all] - Override media type: screen, print…
/// @param {Map} $breakpoints [$govuk-breakpoints] - Map of breakpoints to use
///
/// @content styling rules to use for the given media query range
///
/// @example scss
///  .element {
///    @include govuk-media-query($from: mobile) {
///      color: red;
///    }
///    @include govuk-media-query($until: tablet) {
///      color: blue;
///    }
///    @include govuk-media-query(mobile, tablet) {
///      color: green;
///    }
///    @include govuk-media-query($from: tablet, $and: '(orientation: landscape)') {
///      color: teal;
///    }
///    @include govuk-media-query(950px) {
///      color: hotpink;
///    }
///    @include govuk-media-query(tablet, $media-type: screen) {
///      color: rebeccapurple;
///    }
///  }
///
/// @access public

@mixin govuk-media-query($from: null, $until: null, $and: null, $media-type: "all", $breakpoints: $govuk-breakpoints) {
  $media-query: "";
  $min-width: if($from, govuk-from-breakpoint($from, $breakpoints), null);
  $max-width: if($until, govuk-until-breakpoint($until, $breakpoints), null);

  // Assemble the media query string
  @if $media-type != "all" {
    $media-query: "#{$media-type}";
  }
  @if $min-width {
    $media-query: "#{$media-query} and #{$min-width}";
  }
  @if $max-width {
    $media-query: "#{$media-query} and #{$max-width}";
  }
  @if $and {
    $media-query: "#{$media-query} and #{$and}";
  }

  // If $media-query currently begins with an orphaned ' and ', remove it.
  @if str-index($media-query, " and ") == 1 {
    $media-query: str-slice($media-query, 6);
  }

  @media #{$media-query} {
    @content;
  }
}
